iT邦幫忙

2023 iThome 鐵人賽

DAY 16
0

統整一下 Par 容器型態的 function

經過 昨天 的推導,我們的平行化 library 中定義了 Par[A] 容器型態,然後核心 API 中包含了以下 function,

def unit[A](a:  A): Par[A]
def map2[A, B, C](a: Par[A], b: Par[B])(f: (A, B) => C): Par[C]
def fork[A](a: => Par[A]): Par[A]
def lazyUnit[A](a: => A): Par[A]
def run[A](a: Par[A]): A

我們也簡單定義了每個 function 的職責:

  • unit 使一般的值變成平行化計算。
  • map2 使用 high-order function 把 2 個平行化計算的合併成 1 個平行化計算。
  • fork 將某一個平行化計算從主執行緒 fork 出來,此計算不會馬上運行,直到被 run 觸發。
  • lazyUnit 包裹一個還未計算的表達式成為平行化計算並 fork 它
  • run 實際運行平行化計算並從中提取值

run

run 需要真正的開始執行平行化計算了,我們或許可以自行實現底層 API,但是,Java 標準庫中已經有 java.util.concurrent.ExecutorService 可供我們使用,如果把它翻譯成 Scala 的話 API 定義如下:

trait ExecutorService:
  def submit[A](a: Callable[A]): Future[A]

trait Callable[A]:
  def call: A

trait Future[A]:
  def get: A
  def get(timeout: Long, unit: TimeUnit): A
  def cancel(mayInterruptIfRunning: Boolean): Boolean
  def isDone: Boolean
  def isCancelled: Boolean

ExecutorService 需提交 Callable 然後回傳 Future,Future 隱含著會執行在不同執行緒上,然後透過會等待的 get 取得值,

我們嘗試讓 run 存取 ExecutorService,

def run[A](s: ExecutorService)(a: Par[A]): A

所以 Par[A] 可以想像成代表了 ExecutorService => A,因為 A 代表了要從 Future 中 get,為了更好的推展延遲運行,以及用到 Future 那些 function,我們可以把 Par[A] 型態改成這樣 ExecutorService => Future[A],然後讓我們的 run 回傳 Future,

type Par[A] = ExecutorService => Future[A]

def run[A](s: ExecutorService)(a: Par[A]): Future[A] = a(s)

type 的意思可以參考 Day 13

現在我們的 Par[A] 是 function ExecutorService => Future[A] 的別名,操作 Par 型態也不用擔心會立即建立新的執行緒,除非我們調用 run 並提供 ExecutorService,才會取得實際執行在不同執行緒上的 Future 物件。


明天繼續實作其他 function!


上一篇
Purely Function 的平行化 (1)
下一篇
Purely Function 的平行化 (3)
系列文
用 Scala 3 寫的 Functional Programming 會長什麼樣子?30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言